import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import org.jsoup.select.Elements;

import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Scanner;
import java.util.logging.*;
import java.net.URL;

/**
 * Created by Shevtsov on 05.02.14.
 * Загрузка списка обучающих материалов по немецкому языку с сайта http://biblioteka.cc/
 * use class HttpDownloadUtility //http://stackoverflow.com/questions/921262/how-to-download-and-save-a-file-from-internet-using-java
 */
public class BibliotekLoader {

    private static final Logger logger = Logger.getLogger(BibliotekLoader.class.getName());
//    private static final String downloadRootDirAddress = "D:\\TEMP\\deutschLib\\";
    private static final String downloadRootDirAddress = "D:\\Шевцов\\темп\\135\\";

    public static void main(String[] args) {

        configurateLogger();

        String pageLibraryDeutcsh = "http://biblioteka.cc/index.php?do=cat&category=%D0%9D%D0%B5%D0%BC%D0%B5%D1%86%D0%BA%D0%B8%D0%B9%20%D1%8F%D0%B7%D1%8B%D0%BA";
        ArrayList<Element> links = null;
        ArrayList<Element> scripts = null;
        try {
            logger.log(Level.FINEST, "try to read main library link");
            links = readLibUrlsJSoup(pageLibraryDeutcsh);
            //scripts = readLibJavaScriptsSoup(pageLibraryDeutcsh); //нет смысла выполнять все скрипты здесь. Попробуем на страницах книг
        } catch (IOException e) {
            logger.log(Level.SEVERE, "Can`t read links from main library page");
            System.exit(0);
        }


        //пока что комментируем
        //выведем пронумерованный список ссылок, чтоб можно было выбрать диапазон номеров
        for (int i = 0; i < links.size(); i++) {
            System.out.println(i+1 + "\t" + links.get(i).ownText());
        }
        //выберем необходимый диапазон номеров ссылок для скачивания
        HashMap<String, Integer> firstLastNumber = new HashMap<>();
        firstLastNumber.put("from", 0);
        firstLastNumber.put("to", 0);
        selectRange(firstLastNumber, new Integer(links.size()));
        int fromNumber = firstLastNumber.get("from");
        int toNumber = firstLastNumber.get("to");
        for (int i = fromNumber - 1 ; i < toNumber; i++) {
            processBooksMainPage(links.get(i));
        }
        logger.log(Level.INFO, "That`s OK!");

    }

    static ArrayList<Element> readLibUrlsJSoup(String libraryDeutcshPage) throws IOException {
        ArrayList<Element> result = new ArrayList<>();
        String line;
        Document doc = Jsoup.connect(libraryDeutcshPage).get();
        logger.log(Level.FINEST, "jsoup connected to library");
        Elements links = doc.select("a[href]");
        for(Element link : links){
            if(link.attr("href").startsWith("index.php?newsid=")){
                    logger.log(Level.FINEST, link.attr("href"));
                    logger.log(Level.FINEST, link.ownText());
                    result.add(link);
            }
        }
        return result;
    }

    static ArrayList<Element> readLibJavaScriptsSoup(String libraryDeutcshPage) throws IOException{
        ArrayList<Element> result = new ArrayList<>();
        String line;
        Document doc = Jsoup.connect(libraryDeutcshPage).get();
        logger.log(Level.FINEST, "jsoup connected to library");
        Elements scripts = doc.select("script[type=text/javascript]");
        for (Element js : scripts) {
            result.add(js);
        }
        return result;
    }

    static void downloadLink(Element link){
        //http://stackoverflow.com/questions/921262/how-to-download-and-save-a-file-from-internet-using-java
    }

    private static void configurateLogger(){
        Level commonLevel = Level.INFO;
        logger.setUseParentHandlers(false);
        logger.setLevel(commonLevel);
        Handler finestHandler = new ConsoleHandler();
        Handler commonHandler = new ConsoleHandler();
        finestHandler.setLevel(Level.FINEST);
        finestHandler.setFormatter(new MyFormatter());
        commonHandler.setFormatter(new SimpleFormatter());
        logger.addHandler(finestHandler);
        logger.addHandler(commonHandler);
    }

    private static void processBooksMainPage(Element linkElement){
        String bookDirName = getBookDirName(linkElement.ownText());

        Document doc = null;
        doc = getMainBooksPageDocument(linkElement);
        saveFiles(bookDirName, doc);
    }

    private static Document getMainBooksPageDocument(Element linkElement){
        Document doc = null;
        try {
            logger.log(Level.FINEST, "connecting books title page: " + linkElement.ownText());
            doc = Jsoup.connect("http://biblioteka.cc/" + linkElement.attr("href")).get();
            logger.log(Level.FINEST, "connected books title page: " + linkElement.ownText());
        } catch (IOException e) {
            logger.log(Level.SEVERE, "can`t connect to " + linkElement.attr("href") + " ; " + linkElement.ownText(), e);
            System.exit(0);
        }
        return doc;
    }

    private static String getBookDirName(String dirName){

        if(dirName.endsWith(".")){
            dirName = dirName.substring(0, dirName.length()-2);
        }
        dirName = exchangeGermanSymbols(dirName);
        dirName = dirName.replace(":", "");
        dirName = dirName.replace("/", "");
        dirName = dirName.replace("\"", "");
        dirName = downloadRootDirAddress + dirName;
        logger.log(Level.FINEST, "dir name will be: " + dirName);

        return dirName;

    }

    private static void createBookDir(String dirName) throws IOException {

        File bookDir = new File(dirName);
        if(! bookDir.mkdir()){
            logger.info("directory " + dirName + " can`t be created");
//            throw new IOException();
        }else {
            logger.log(Level.FINEST, "book dir " + dirName + " created");
        }
    }

    private static String exchangeGermanSymbols(String string){
        char[] symbols = string.toCharArray();
        for (int i = 0; i < symbols.length; i++) {
            switch (symbols[i]){
                case 'ö' : symbols[i] = 'o';
                    break;
                case 'Ö' : symbols[i] = 'O';
                    break;
                case 'ä' : symbols[i] = 'a';
                    break;
                case 'Ä' : symbols[i] = 'A';
                    break;
                case 'ü' : symbols[i] = 'u';
                    break;
                case 'Ü' : symbols[i] = 'U';
                    break;
            }
            string = new String(symbols);
            string = string.replace("ß", "ss");
        }
        return string;
    }

    private static void printChildsText(Node node){
//        Elements myElements = myElement.children();
//        for(Element element : myElements){
//            if(!element.ownText().isEmpty()){
//                System.out.println(element.tagName() + element.ownText());
//            }
//            printChildsText(element);
//        }
        if (node.hasAttr("text")){
            String textAttr = node.attr("text");
            if(!textAttr.isEmpty()){
                System.out.println(textAttr);
            }
        }
        List<Node> childNodes = node.childNodes();
//        for (Node childNode : childNodes){
//            printChildsText(childNode);
//        }
    }

    private static void openInnerURLsInBrowser(Document doc) {

        Elements refs = doc.select("a[href]:matchesOwn(zip|pdf|djvu)");

        for(Element ref : refs){

            String href = ref.attr("href");
//         "http://biblioteka.cc/" + ref.attr("href");
//            System.out.println(ref.ownText() + "  " + href);
            logger.info("opening in browser " + ref.ownText() + "  " + href);

            openURLInBrowser(ref);

            Document downLoadPage = null;
            try {
                logger.log(Level.FINEST, "try to connect " + href);
                downLoadPage = Jsoup.connect(href).get();
                logger.log((Level.FINEST), "connected to " + href);
            } catch (IOException e) {
                logger.log(Level.SEVERE, "can't connect to " + href);
                System.exit(0);
            }
            Element finish = null;
            Elements skachatElements = downLoadPage.getElementsMatchingOwnText("СКАЧАТЬ");
            if (skachatElements.size() == 1){
                finish = skachatElements.get(0);
            }else if (skachatElements.size() > 1){
                logger.log(Level.INFO, "on page " + href + " thre are more than 1 download links");
                System.exit(0);
            }else {
                logger.log(Level.INFO, "on page " + href + " there is no download links");
                continue;
            }
            openURLInBrowser(finish);

        }

    }

    private static void saveFiles(String bookDirName, Document doc){

        Elements refs = doc.select("a[href]:matchesOwn(zip|pdf)");

        for(Element ref : refs){
            String href = ref.attr("href");
//                    "http://biblioteka.cc/" + ref.attr("href");
            System.out.println(ref.ownText() + "  " + href);
            Document downLoadPage = null;
            try {
                logger.log(Level.FINEST, "try to connect " + href);
                downLoadPage = Jsoup.connect(href).get();
                logger.log((Level.FINEST), "connected to " + href);
            } catch (IOException e) {
                logger.log(Level.SEVERE, "can't connect to " + href);
                System.exit(0);
            }
            Element finish = null;
            Elements skachatElements = downLoadPage.getElementsMatchingOwnText("СКАЧАТЬ");
            if (skachatElements.size() == 1){
                finish = skachatElements.get(0);
            }else if (skachatElements.size() > 1){
                logger.log(Level.INFO, "on page " + href + " thre are more than 1 download links");
                System.exit(0);
            }else {
                logger.log(Level.INFO, "on page " + href + " there is no download links");
                continue;
            }
            String link = finish.attr("href");

            ///
            try {
                logger.log(Level.FINEST, "creating book dir " + bookDirName);
                createBookDir(bookDirName);

            } catch (IOException e) {
                logger.log(Level.SEVERE, "book dir " + bookDirName + " was not created", e);
                System.exit(0);
//                continue;
            }
            ///
            try {
                logger.log(Level.FINEST, "trying to save file " + link);
                HttpDownloadUtility.downloadFile(link, bookDirName);
            } catch (IOException e) {
                logger.log(Level.SEVERE, "cant save file", e);
            }

        }

    }

    private static void selectRange(HashMap<String, Integer> firstLastNumber, int linksSize){
        Integer fromNumber  = firstLastNumber.get("from");
        Integer toNumber    = firstLastNumber.get("to");
        boolean incorrectRange = true;
        while (incorrectRange){
            Scanner scanner = new Scanner(System.in);
            System.out.print("С какого номера читаем: ");
            fromNumber = scanner.nextInt();
            System.out.println();
            System.out.println("По какой номер читаем: ");
            toNumber = scanner.nextInt();

            if(fromNumber > 0){
                incorrectRange = false;
            }else {
                incorrectRange = true;
                logger.log(Level.INFO, "number of the first link must be greater than 0");
            }

            if(toNumber < fromNumber){
                incorrectRange = true;
                logger.log(Level.INFO, "number of the last link must be less then number of the first link");
            }else if(toNumber > linksSize){
                incorrectRange = true;
                logger.log(Level.INFO, "number of the last link must be less then total amount of links");
            }else {
                incorrectRange = false;
            }
            if (incorrectRange){
                logger.log(Level.INFO, "input range once again:");
            }else {
                firstLastNumber.put("from", fromNumber);
                firstLastNumber.put("to", toNumber);
            }
        }

    }

    private static void openURLInBrowser(Element link) {
        String prefix = "http://biblioteka.cc/";
        String urlStr = link.attr("href");
        if (!urlStr.startsWith(prefix)) {
            urlStr = prefix.concat(urlStr);
        }
        URI uri = null;
        try {
            uri = new URI(urlStr);
        } catch (URISyntaxException e) {
            logger.log(Level.WARNING, "cant create uri to open in browser", e);
        }
        Desktop desktop = Desktop.getDesktop();
        try {
            desktop.browse(uri);
        } catch (IOException e) {
            logger.log(Level.WARNING, "can`t open in browser", e);
        }

    }
}


